﻿using System;
using System.Collections.Generic;
using System.Text;
using EthernetTools.Utils;

namespace EthernetTools.Protocols.PPPoE
{
    public class PPPoETag : Object
    {
        private byte[] _data;
        private byte[] _value;

        public bool IsEnabled
        {
            get;
            set;
        }

        public bool IsExpanded
        {
            get;
            set;
        }

        /// <summary>
        /// String value of tag (NOTE: IT CAN BE TRASH CAUSE CONTAINS BINARY DATA, CHECK TagType and use it or RawData)
        /// </summary>
        public string Value
        {
            get 
            {
                StringBuilder sb = new StringBuilder();
                foreach (byte b in _value)
                {
                    sb.Append((char)b);
                }
                return sb.ToString();
            }
            
        }

        /// <summary>
        /// Byte[] value of tag
        /// </summary>
        public byte[] ByteValue
        {
            get
            {
                return _value;
            }

        }
        public TagType ThisType
        {
            get;
            private set;
        }
        
        public enum TagType
        {
            /// <summary>
            /// 0x0000 EndOfList
            /// This TAG indicates that there are no further TAGs in the list. The
            /// TAG_LENGTH of this TAG MUST always be zero.  Use of this TAG is
            /// not required, but remains for backwards compatibility.
            /// </summary>
            EndOfList = 0x0000,

            /// <summary>
            ///  0x0101 Service-Name
            ///  his TAG indicates that a service name follows.  The TAG_VALUE is
            ///  an UTF-8 string that is NOT NULL terminated. When the TAG_LENGTH
            ///  is zero this TAG is used to indicate that any service is
            ///  acceptable.  Examples of the use of the Service-Name TAG are to
            ///  indicate an ISP name or a class or quality of service.
            /// </summary>
            ServiceName = 0x0101,

            /// <summary>
            /// 0x0102 AC-Name
            /// This TAG indicates that a string follows which uniquely identifies
            /// this particular Access Concentrator unit from all others. It may
            /// be a combination of trademark, model, and serial id information,
            /// or simply an UTF-8 rendition of the MAC address of the box.  It is
            /// not NULL terminated.
            /// </summary>
            ACName = 0x0102,

            /// <summary>
            /// 0x0103 Host-Uniq
            /// This TAG is used by a Host to uniquely associate an Access
            /// Concentrator response (PADO or PADS) to a particular Host request
            /// (PADI or PADR).  The TAG_VALUE is binary data of any value and
            /// length that the Host chooses.  It is not interpreted by the Access
            /// Concentrator.  The Host MAY include a Host-Uniq TAG in a PADI or
            /// PADR.  If the Access Concentrator receives this TAG, it MUST
            /// include the TAG unmodified in the associated PADO or PADS
            /// response.
            /// </summary>
            HostUniq = 0x0103,

            /// <summary>
            /// 0x0104 AC-Cookie
            /// This TAG is used by the Access Concentrator to aid in protecting
            /// against denial of service attacks (see the Security Considerations
            /// section for an explanation of how this works).  The Access
            /// Concentrator MAY include this TAG in a PADO packet.  If a Host
            /// receives this TAG, it MUST return the TAG unmodified in the
            /// following PADR.  The TAG_VALUE is binary data of any value and
            /// length and is not interpreted by the Host.
            /// </summary>
            ACCookie = 0x0104,

            /// <summary>
            /// 0x0105 Vendor-Specific
            /// This TAG is used to pass vendor proprietary information.  The
            /// first four octets of the TAG_VALUE contain the vendor id and the
            /// remainder is unspecified.  The high-order octet of the vendor id
            /// is 0 and the low-order 3 octets are the SMI Network Management
            /// Private Enterprise Code of the Vendor in network byte order, as
            /// defined in the Assigned Numbers RFC [4].
            /// 
            /// Use of this TAG is NOT RECOMMENDED.  To ensure inter-operability,
            /// an implementation MAY silently ignore a Vendor-Specific TAG.
            /// </summary>
            VendorSpecific = 0x0105,

            /// <summary>
            /// 0x0110 Relay-Session-Id
            /// This TAG MAY be added to any discovery packet by an intermediate
            /// agent that is relaying traffic.  The TAG_VALUE is opaque to both
            /// the Host and the Access Concentrator.  If either the Host or
            /// Access Concentrator receives this TAG they MUST include it
            /// unmodified in any discovery packet they send as a response.  All
            /// PADI packets MUST guarantee sufficient room for the addition of a
            /// Relay-Session-Id TAG with a TAG_VALUE length of 12 octets.
            /// 
            /// A Relay-Session-Id TAG MUST NOT be added if the discovery packet
            /// already contains one.  In that case the intermediate agent SHOULD
            /// use the existing Relay-Session-Id TAG.  If it can not use the
            /// existing TAG or there is insufficient room to add a Relay-
            /// Session-Id TAG, then it SHOULD return a Generic-Error TAG to the
            /// sender.
            /// </summary>
            RelaySessionId = 0x0110,

            /// <summary>
            /// 0x0201  Service-Name-Error
            /// This TAG (typically with a zero-length data section) indicates
            /// that for one reason or another, the requested Service-Name request
            /// could not be honored.
            /// 
            /// If there is data, and the first octet of the data is nonzero, then
            /// it MUST be a printable UTF-8 string which explains why the request
            /// was denied.  This string MAY NOT be NULL terminated.
            /// </summary>
            ServiceNameError = 0x0201,

            /// <summary>
            ///  0x0202 AC-System-Error
            ///  This TAG indicates that the Access Concentrator experienced some
            ///  error in performing the Host request.  (For example insufficient
            ///  resources to create a virtual circuit.)  It MAY be included in
            ///  PADS packets.
            /// </summary>
            ACSystemError = 0x0202,

            /// <summary>
            /// 0x0203 Generic-Error
            /// If there is data, and the first octet of the data is nonzero, then
            /// it MUST be a printable UTF-8 string which explains the nature of
            /// the error.  This string MAY NOT be NULL terminated.
            /// * 
            /// This TAG indicates an error.  It can be added to PADO, PADR or
            /// PADS packets when an unrecoverable error occurs and no other error
            /// TAG is appropriate.  If there is data then it MUST be an UTF-8
            /// string which explains the nature of the error.  This string MUST
            /// NOT be NULL terminated.
            /// 
            /// </summary>
            GenericError = 0x0203
        }

        #region Properties

        /// <summary>
        /// Tag raw data
        /// </summary>
        public byte[] Data
        {
            get
            {
                return _data;
            }
        }

        #endregion

        #region Constructors and Destructors
        
        /// <summary>
        /// Creates a PPPoE tag
        /// </summary>
        /// <param name="type">Tag type see: TagType</param>
        /// <param name="data">Tag data</param>
        public PPPoETag (TagType type, byte[] tdata, bool isExpanded = false)
        {
            IsEnabled = true;

            IsExpanded = isExpanded;

            ThisType = type;
            _data = new byte[0];

            byte[] tType = new byte[] { (byte)((int)type >> 8), (byte)((int)type & 0x00FF) };

            byte[] tLen;
            if (tdata != null)
            {
                tLen = new byte[] { (byte)((int)tdata.Length >> 8), (byte)((int)(int)tdata.Length & 0x00FF) };
            }
            else
            {
                tLen = new byte[] { 0x00, 0x00 };
            }

            ByteUtils.AppendBytes(ref _data, tType);
            ByteUtils.AppendBytes(ref _data, tLen);
            ByteUtils.AppendBytes(ref _data, tdata);
            _value = tdata;
        }

        #endregion

        #region Public Methods

        public override string ToString()
        {
            string retVal = ThisType.ToString() + " : " + (Data.Length - 2) + " - ";

            retVal += "0x" + ByteUtils.ByteArrToString(_data, ";0x");

            return retVal; 
        }

        public string ValueLength()
        {
            if (Data != null)
                return (Data.Length - 4).ToString();
            else
                return "0";
        }

        public string StringData()
        {
            return ByteUtils.ByteArrToString(_data, ":");
        }
        #endregion
    }
}
